import buffer from '@ohos.buffer';
import * as system from './system';
import { STATE_TYPE } from './constant';
import { nfcConfig } from '../../config';
import { log, waitTime, promiseWithResolvers } from '../../util/index';

import type { MyTCPSocket } from './system';
import type { NetAddress, MessageHandler, Message } from './type';

const TAG = 'message_wifi_service'

class WifiMessage {
  private maxWaitStateNum = 10;
  private tcpInstance: MyTCPSocket;
  private clientNetAddress: NetAddress = {
    address: '0.0.0.0',
    port: 9527
  }
  private serverNetAddress: NetAddress = {
    address: '192.168.43.1',
    port: 9528
  }
  private connectTimeout: number = 30 * 1000
  private messageHandlerMap: Map<string | symbol, MessageHandler> = new Map()
  private authResultHandler: MessageHandler<'0' | '1'> = null

  private async waitState(type: STATE_TYPE): Promise<boolean> {
    let state = await this.tcpInstance.getState()
    let currentState = state[type]
    log.info(TAG, 'waitState', type, currentState)
    let num = 0
    while (num < this.maxWaitStateNum && !currentState) {
      await waitTime()
      state = await this.tcpInstance.getState()
      currentState = state[type]
      log.info(TAG, 'waitState', type, currentState)
      num = num + 1
    }
    return currentState
  }

  private async bind(): Promise<boolean> {
    let result = await this.tcpInstance.bind(this.clientNetAddress)
    if (result) {
      result = await this.waitState(STATE_TYPE.BOUND)
    }
    log.info(TAG, 'bind', result)
    return result
  }

  private async connect(): Promise<boolean> {
    let result = await this.tcpInstance.connect({ address: this.serverNetAddress, timeout: this.connectTimeout })
    if (result) {
      result = await this.waitState(STATE_TYPE.CONNECTED)
    }
    log.info(TAG, 'connect', result)
    return result
  }

  private receiveMessage(): boolean {
    const result = this.tcpInstance.onMessage((value) => {
      const messageStr = buffer.from(value.message).toString()
      try {
        const message = JSON.parse(messageStr) as Message
        if (message.type === 1 && this.authResultHandler) {
          this.authResultHandler(message as Message<'0' | '1'>)
        }
        if (message.type !== 1) {
          this.messageHandlerMap.forEach((handler) => {
            handler(message)
          })
        }
      } catch (err) {
        log.error(TAG, 'receiveMessage json error')
      }
    })
    return result
  }

  async create(serverAddress: string): Promise<boolean> {
    this.tcpInstance = system.createTCPInstance()
    this.serverNetAddress.address = serverAddress
    let result = await this.bind()
    if (result) {
      result = await this.connect()
    }
    if (result) {
      this.receiveMessage()
      this.tcpInstance.onConnect(() => {
        log.info(TAG, 'onConnect')
      })
      this.tcpInstance.onClose(() => {
        log.info(TAG, 'onClose')
      })
      this.tcpInstance.onError(() => {
        log.info(TAG, 'onError')
      })
    } else {
      this.tcpInstance = null
    }
    log.info(TAG, 'create', result)
    return result
  }

  async destroy(): Promise<boolean> {
    let result = false
    if (this.tcpInstance) {
      const state = await this.tcpInstance.getState()
      if (state.isConnected) {
        let result = await this.tcpInstance.close()
        if (result) {
          result = await this.waitState(STATE_TYPE.CLOSE)
        }
      }
      this.tcpInstance = null
    } else {
      result = true
    }
    return result
  }

  async sendMessage(message: string): Promise<boolean> {
    if (this.tcpInstance) {
      const state = await this.tcpInstance.getState()
      if (state.isConnected) {
        const result = await this.tcpInstance.send({
          data: message
        })
        return result
      }
    }
    return false
  }

  async auth(): Promise<boolean> {
    const {promise, resolve} = promiseWithResolvers<boolean>()
    const timeoutTimer = setTimeout(() => {
      this.authResultHandler = null
      resolve(false)
    }, 5000)
    this.authResultHandler = (message) => {
      clearTimeout(timeoutTimer)
      if (message.data === '1') {
        resolve(true)
      } else {
        resolve(false)
      }
    }
    const authMessage: Message = {
      type: 1,
      data: nfcConfig.secret
    }
    this.sendMessage(JSON.stringify(authMessage)).then((result) => {
      if (!result) {
        this.authResultHandler = null
        clearTimeout(timeoutTimer)
        resolve(false)
      }
    })
    return promise
  }

  omMessage(handler: MessageHandler, key: string | symbol = Symbol()) {
    this.messageHandlerMap.set(key, handler)
  }

  offMessage(key?: string) {
    if (key === undefined) {
      this.messageHandlerMap.clear()
    } else {
      if (this.messageHandlerMap.has(key)) {
        this.messageHandlerMap.delete(key)
      }
    }
  }
}

const wifiMessage = new WifiMessage()

export {
  wifiMessage
}

export type {
  MessageHandler
}

